#pragma once

#include <ddblk/apds/combine_int.h>
#include <dbg.h>
/* combine 算法 */

typedef struct _ccsyncnode
{
	APDSFUTURE_DECL
	struct _ccsyncnode *next;
} _ccsyncnode_t;

typedef struct _ccsync
{
	_ccsyncnode_t * tail;
} _ccsync_t;

DECL_COMBINE(ccsync)

/* 实现接口 */

SAI void __ccsync_init(_ccsync_t *s, _ccsyncnode_t *sentry)
{
	s->tail = sentry;
	sentry->next = NULL;
	sentry->flag = COMBINE_FLG_OWNED;
}

SAI void *__ccsync_destruct(_ccsync_t *s, _ccsyncnode_t **psentry)
{
	ASSERT(s->tail != NULL);
	*psentry = s->tail;
	return (void *)s;
}

SAI _ccsyncnode_t *__ccsync_fetch(_ccsync_t *s, apdsfunc_t *pfunc, apdsarg_t *args, _ccsyncnode_t *node){
	apds_movargs(&node->args, args);
	node->func = pfunc;

	/* 确保已经清空 */
	node->flag = COMBINE_FLG_WAIT;
	node->next = NULL;

#ifdef COMBINEDIRECT_ENQUEUE
	_ccsyncnode_t *cur_node = swp_rel(&s->tail, node);
	atm_st(&cur_node->next, node);
	return cur_node;
#else
	PARAM_USED(s);
	return node;
#endif
}

SAI apdsret_t __ccsync_wait(_ccsync_t *s, _ccsyncnode_t **pnode, INFUN(cmbn_wait, waitpolicy)){
	waitcnt_t cnt = -1;
	lkword_t flagwait = COMBINE_FLG_WAIT;
	_ccsyncnode_t *next_node, *cur_node, *tmp_node, *tmp_node_next;
	lkword_t flag;
	hword_t help_count = 0;

#ifdef COMBINEDIRECT_ENQUEUE
	PARAM_USED(s);
	next_node = (*pnode)->next;
	cur_node = *pnode;
#else
	next_node = *pnode;
	cur_node = swp_rel(&s->tail, next_node);
	atm_st(&cur_node->next, next_node);
#endif
	flag = atm_ldacq(&cur_node->flag);
	while (flag == COMBINE_FLG_WAIT)
	{
		cnt = waitpolicy->waitf(&cur_node->flag, &flagwait, cnt);
		flag = atm_ldacq(&cur_node->flag);
	}

	if (flag == COMBINE_FLG_COMPLETED)
	{
		/* mp_rmb(); */ /* 同一cache行且存在读依赖 */
		*pnode = cur_node;
		return apds_args_ret(&cur_node->args);
	}

	/* mp_rmb(); */

	tmp_node = cur_node;
	tmp_node_next = next_node;
	
	do
	{
		/* APDS_PREFETCH(tmp_node_next); */
		// 帮助
		/* cmb(); */
		apds_set_ret(&tmp_node->args, apds_sync_single(tmp_node_next->func, &tmp_node_next->args));
		atm_strel(&tmp_node->flag, COMBINE_FLG_COMPLETED);
		tmp_node = tmp_node_next;
		if (++help_count >= APDS_HELP_QUOTA) break;
		tmp_node_next = tmp_node_next->next;
	} while (tmp_node_next);

	/* 释放所有权 */
	atm_strel(&tmp_node->flag, COMBINE_FLG_OWNED);
	/* 唤醒线程 */
	lkword_t flag_owned = COMBINE_FLG_OWNED;
	waitpolicy->wakef(&tmp_node->flag, &flag_owned);

	*pnode = cur_node;
	return apds_args_ret(&cur_node->args);
}

PARAM_NONULL(1) SAI _ccsync_t *_ccsync_init(void *ptr, size_t size, INFUN(cmbn_node, nodemgr))
{
	if (size < sizeof(_ccsync_t)) return NULL;
	_ccsyncnode_t *node = (_ccsyncnode_t *)nodemgr->get_lknode(sizeof(_ccsyncnode_t));
	_ccsync_t *s = (_ccsync_t *)ptr;
	__ccsync_init(s, node);
	return s;
}
PARAM_NONULL(1) SAI void *_ccsync_destruct(_ccsync_t *s, INFUN(cmbn_node, nodemgr)){
	_ccsyncnode_t *node;
	void *rptr = __ccsync_destruct(s, &node);
	nodemgr->ret_lknode(node, sizeof(_ccsyncnode_t));
	return rptr;
}
PARAM_NONULL(1, 2, 3) SAI _ccsyncnode_t *_ccsync_fetch(_ccsync_t *s, apdsfunc_t *pfunc, apdsarg_t *args, apdstinfo_t *tinfo, INFUN(cmbn_node, nodemgr)){
	PARAM_USED(tinfo);
	_ccsyncnode_t *node = (_ccsyncnode_t *)nodemgr->get_lknode(sizeof(_ccsyncnode_t));
	return __ccsync_fetch(s, pfunc, args, node);
}
PARAM_NONULL(1, 2) SAI apdsret_t _ccsync_wait(_ccsync_t *s, _ccsyncnode_t *node, apdstinfo_t *tinfo, INFUN(cmbn_wait, waitpolicy), INFUN(cmbn_node, nodemgr)){
	PARAM_USED(tinfo);
	apdsret_t ret = __ccsync_wait(s, &node, waitpolicy);
	nodemgr->ret_lknode(node, sizeof(_ccsyncnode_t));
	return ret;
}

